package com.wyj.service.impl;

import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.Policy;
import com.github.benmanes.caffeine.cache.stats.CacheStats;
import com.wyj.dao.UserDao;
import com.wyj.pojo.GameUser;
import com.wyj.service.UserService;
import com.wyj.util.RedisKeyUtil;
import lombok.extern.log4j.Log4j2;
import lombok.val;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
@Log4j2
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    @Autowired
    RedisKeyUtil redisKeyUtil;
    @Autowired
    RedisTemplate redisTemplate;

    @Value("${caffeine.gets.max-size}")
    private int maxSize;

    @Value("${caffeine.gets.expire-seconds}")
    private int expireTime;

    //设置缓存变量
    private LoadingCache<String,GameUser> gameUserCache;

    //初始化本地缓存 初始化完成后 gameUserCache 就可以在本类当中被随意调用了。
    @PostConstruct
    public void init() {
        gameUserCache = Caffeine.newBuilder()
                .maximumSize(maxSize)
                .expireAfterWrite(expireTime,TimeUnit.MINUTES)
                .build(new CacheLoader<String, GameUser>() {

                    //当本地缓存没有的时候，我们要去redis或者DB中进行查询。
                    @Override
                    public @Nullable GameUser load(@NonNull String s) throws Exception {

                        if (s == null || s.length() == 0)
                            throw new IllegalArgumentException("加载本地缓存的长度为0");

                        log.info("load info is redis");
                        //查缓存
                        String gameUserKey = redisKeyUtil.getGameUserKey();
                        GameUser gameUser = (GameUser) redisTemplate.opsForHash().get(gameUserKey, s);

                        //查库
                        if (gameUser == null) {

                            log.info("load info is DB");

                            gameUser = userDao.findGameUserByUUID(s);
                            //数据库中不存在的话，那么就重新创建一个
                            if (gameUser == null) {
                                gameUser = new GameUser();
                                gameUser.setUserName("wyj"+s.substring(0,2));
                                gameUser.setGoldCoin(1000L);
                                gameUser.setUserLevel(100L);
                                gameUser.setVipLevel(100L);
                                gameUser.setUUID(s);

                                userDao.save(gameUser);
                            }
                            //写入缓存
                            redisTemplate.opsForHash().put(gameUserKey,s,gameUser);
                            //设置过期时间 3天
                            redisTemplate.expire(gameUserKey,3, TimeUnit.DAYS);
                        }

                        return gameUser;
                    }
                });
    }


    /**
     *用户登录
     * 查缓存
     * 查库
     * 写缓存
     */
    @Override
    public GameUser getGameUserByUUID(String uuid) {

        val gameUser = gameUserCache.get(uuid);

        return gameUser;
    }

    /**
     * 登录逻辑
     * @param userName
     * @return
     */
    @Override
    public String getGameUserByUserName(String userName) {

        GameUser gameUserByUserName = userDao.findGameUserByUserName(userName);

        //登陆成功
        if(gameUserByUserName != null) {
            //获取该用户的UUID,将其存入token
            String uuid = gameUserByUserName.getUUID();
            String token = redisKeyUtil.getToken(uuid);
            //将每一次登录成功的用户 产生一个对应的token 每一个用户用UUID来唯一标识，设置过期时间为5天
            redisTemplate.opsForValue().set(token,gameUserByUserName,5,TimeUnit.DAYS);

            //返回uuid给前端
            return uuid;
        }

        return "登录信息不准确 请重新输入";
    }

}
